import axios, { AxiosInstance, AxiosResponse, AxiosError, CancelTokenSource } from 'axios';
import { 
  RequestConfig, 
  RequestInterceptors, 
  RequestMethod, 
  ResponseData,
  HttpError
} from './types';
import {
  defaultConfig,
  currentEnvConfig,
} from './config';
import {
  createHttpError,
  handleHttpStatusError,
  handleResponseData,
  printRequestLog,
  printResponseLog,
  printErrorLog,
  retry,
  createCancelToken,
  cancelRequest
} from './helpers';

class HttpRequest {
  // Axios 实例
  private instance: AxiosInstance;
  // 拦截器
  private interceptors?: RequestInterceptors;
  // 全局加载状态
  private static loading: any = null;

  constructor(config: RequestConfig) {
    // 创建 axios 实例
    this.instance = axios.create({
      ...config,
      // 允许跨域携带 cookie
      withCredentials: false
    });
    this.interceptors = config.interceptors;
    
    // 注册通用拦截器
    this.setupInterceptors();
  }

  // 设置拦截器
  private setupInterceptors() {
    // 请求拦截器
    this.instance.interceptors.request.use(
      (config) => {
        // 打印请求日志
        printRequestLog(config as RequestConfig);
        
        // 设置 loading
        const requestConfig = config as RequestConfig;
        if (requestConfig.showLoading) {
          // 这里可以集成 Element Plus 或其他 UI 框架的 loading 组件
          // HttpRequest.loading = ElLoading.service({
          //   text: requestConfig.loadingText || '加载中...',
          //   background: 'rgba(0, 0, 0, 0.5)'
          // });
        }

        // 获取 token 并设置请求头
        const token = localStorage.getItem('token');
        if (token) {
          if (!config.headers) {
            config.headers = {};
          }
          // 使用类型断言
          (config.headers as Record<string, string>).Authorization = `Bearer ${token}`;
        }

        // 处理 mockURL
        if (requestConfig.useMock && currentEnvConfig.mockURL) {
          // 修改 baseURL 为 mockURL
          config.baseURL = currentEnvConfig.mockURL;
        }

        // 设置取消令牌
        if (requestConfig.cancelTokenId) {
          config.signal = createCancelToken(requestConfig.cancelTokenId);
        }
        
        // 调用自定义请求拦截器
        if (this.interceptors?.requestInterceptor) {
          config = this.interceptors.requestInterceptor(config);
        }
        
        return config;
      },
      (error) => {
        // 调用自定义请求错误拦截器
        if (this.interceptors?.requestInterceptorCatch) {
          return this.interceptors.requestInterceptorCatch(error);
        }
        return Promise.reject(error);
      }
    );

    // 响应拦截器
    this.instance.interceptors.response.use(
      (response) => {
        // 打印响应日志
        printResponseLog(response);

        // 关闭 loading
        const requestConfig = response.config as RequestConfig;
        if (requestConfig.showLoading && HttpRequest.loading) {
          // HttpRequest.loading.close();
        }

        // 调用自定义响应拦截器
        if (this.interceptors?.responseInterceptor) {
          return this.interceptors.responseInterceptor(response);
        }

        // 是否需要转换响应数据
        if (requestConfig.transform !== false) {
          try {
            return handleResponseData(response);
          } catch (error) {
            return Promise.reject(error);
          }
        }

        return response;
      },
      (error: AxiosError) => {
        // 关闭 loading
        if (HttpRequest.loading) {
          // HttpRequest.loading.close();
        }

        // 请求取消
        if (axios.isCancel(error)) {
          return Promise.reject(createHttpError('请求已取消', 'CANCELED', error));
        }

        // 请求重试
        const config = error.config as RequestConfig;
        if (config?.retry) {
          return retry(config, error);
        }

        // 处理错误
        const httpError = handleHttpStatusError(error);
        
        // 打印错误日志
        printErrorLog(httpError);

        // 调用自定义响应错误拦截器
        if (this.interceptors?.responseInterceptorCatch) {
          return this.interceptors.responseInterceptorCatch(error);
        }

        // 返回错误
        return Promise.reject(httpError);
      }
    );
  }

  // 发起请求
  private request<T = any>(config: RequestConfig): Promise<T> {
    return new Promise((resolve, reject) => {
      this.instance
        .request<any, T>(config)
        .then((res) => {
          resolve(res);
        })
        .catch((err) => {
          reject(err);
        });
    });
  }

  // GET 请求
  public get<T = any>(url: string, params?: any, config?: RequestConfig): Promise<T> {
    return this.request<T>({
      ...config,
      url,
      method: RequestMethod.GET,
      params
    });
  }

  // POST 请求
  public post<T = any>(url: string, data?: any, config?: RequestConfig): Promise<T> {
    return this.request<T>({
      ...config,
      url,
      method: RequestMethod.POST,
      data
    });
  }

  // PUT 请求
  public put<T = any>(url: string, data?: any, config?: RequestConfig): Promise<T> {
    return this.request<T>({
      ...config,
      url,
      method: RequestMethod.PUT,
      data
    });
  }

  // DELETE 请求
  public delete<T = any>(url: string, data?: any, config?: RequestConfig): Promise<T> {
    return this.request<T>({
      ...config,
      url,
      method: RequestMethod.DELETE,
      data
    });
  }

  // PATCH 请求
  public patch<T = any>(url: string, data?: any, config?: RequestConfig): Promise<T> {
    return this.request<T>({
      ...config,
      url,
      method: RequestMethod.PATCH,
      data
    });
  }

  // 上传文件 (包装为 FormData)
  public upload<T = any>(url: string, file: File, name: string = 'file', data?: Record<string, any>, config?: RequestConfig): Promise<T> {
    const formData = new FormData();
    formData.append(name, file);
    
    if (data) {
      Object.keys(data).forEach(key => {
        formData.append(key, data[key]);
      });
    }
    
    return this.post<T>(url, formData, {
      ...config,
      headers: {
        'Content-Type': 'multipart/form-data',
        ...(config?.headers || {})
      }
    });
  }

  // 下载文件 (Blob 响应)
  public download(url: string, params?: any, config?: RequestConfig): Promise<Blob> {
    return this.request<Blob>({
      ...config,
      url,
      method: RequestMethod.GET,
      params,
      responseType: 'blob'
    });
  }

  // 取消请求
  public static cancelRequest(token: string): boolean {
    return cancelRequest(token);
  }

  // 取消所有请求
  public static cancelAllRequests(): void {
    if (!window.__HTTP_CANCEL_TOKENS__) return;
    
    Object.keys(window.__HTTP_CANCEL_TOKENS__).forEach(token => {
      cancelRequest(token);
    });
  }
}

// 创建默认实例
const http = new HttpRequest(defaultConfig);

export {
  HttpRequest,
  http,
  cancelRequest
};

export default http; 